package ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.bitwise.visitors;

import java.util.Stack;

import org.apache.commons.lang.Validate;

import ar.edu.itba.ia2010.geneticos.motor.api.sintaxistree.Expresion;

/**
 * Evalua un arbol de expresiones; de operaciones binarias con todas las variables
 * al mismo tiempo obtienendo la tabla de verdad. 
 * 
 * @author Juan F. Codagnone
 * @since Mar 29, 2010
 */
public class EvalVisitorCallback implements Expresion.VisitorCallback {
    private final boolean [][]cases;
    
    /** Creates the EvalVisitorCallback. */
    public EvalVisitorCallback(final int variables) {
        this(generateValoresDominio(variables));
    }

    /** evalua la expresion */
    public final boolean[] eval(final Expresion e) {
        e.posorder(this);
        if(queue.size() != 1) {
            throw new IllegalStateException("la cola no puede estar vacia");
        }
        return queue.pop();
    }
    
    /** Creates the EvalVisitorCallback. */
    public EvalVisitorCallback(final boolean [][]cases) {
        this.cases = cases;
    }
    
    /** genera los valores del dominio de la tabla de verdad */
    public static boolean[][] generateValoresDominio(final int variables) {
        Validate.isTrue(variables  > 0);
        final boolean [][] cases = new boolean[2 << (variables - 1)][];
        for(int i = 0; i < cases.length; i++) {
            cases[i] = new boolean[variables];
            for(int bit = 0; bit < variables; bit++) {
                cases[i][variables - bit - 1] = (i & (1 << bit)) != 0;
            }
        }
        return cases;
    }
    
    private final Stack<boolean[]> queue = new Stack<boolean[]>();
    /** @see VisitorCallback#notify(Expresion) */
    public final Expresion notify(final Expresion e) {
        Expresion r = null;
        
        if(e.isTerminal()) {
            final boolean []tmp = new boolean[cases.length];
            for(int i = 0; i < tmp.length; i++) {
                tmp[i] = e.recursiveEvaluation(cases[i]);
            }
            queue.add(tmp);
        } else {
            boolean [][]args = new boolean[e.getSubExpresions()][];
            for(int i = 0; i < args.length; i++) {
                args[i] = queue.pop();
            }
            r = onEval(e, args);
            boolean []ret = args[0]; // reuso // new boolean[values];
            boolean [] call = new boolean[args.length];
            for(int i = 0; i < args[0].length; i++) {
                for(int j = 0; j < call.length; j++) { 
                    call[j] = args[j][i];
                }
                ret[i] = e.eval(call);
            }
            queue.add(ret);
        }
        return r;
    }
    
    /** template method para reducir la expresion */
    protected Expresion onEval(final Expresion e, final boolean[][]args) {
        return null;
    }
}
